<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>设备在线状态</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; background: #f7f9fa; }
        h1 { margin-bottom: 4px; }
        table { border-collapse: collapse; width: 100%; background: #fff; }
        th, td { border: 1px solid #d0d7de; padding: 8px; text-align: left; vertical-align: top; }
        th { background: #f0f3f5; }
        .online { background: #e6ffed; color: #1a7f37; font-weight: bold; padding: 2px 6px; border-radius: 4px; }
        .offline { background: #ffebe9; color: #cf222e; font-weight: bold; padding: 2px 6px; border-radius: 4px; }
        .small { font-size: 12px; color: #57606a; margin-bottom: 10px; }
        .reg-box { font-family: monospace; font-size: 12px; white-space: nowrap; }
        .hex-box { font-family: monospace; font-size: 11px; white-space: pre-wrap; line-height: 1.4; }
        #error { color: #cf222e; font-size: 12px; margin-top: 8px; }
        footer { margin-top: 20px; font-size: 12px; color: #57606a; }
    </style>
</head>
<body>
<h1>设备在线监控</h1>
<div class="small" id="generatedAt">加载中...</div>
<div id="error"></div>
<table id="deviceTable">
    <thead>
    <tr>
        <th>设备地址</th>
        <th>状态</th>
        <th>最近成功时间</th>
        <th>连续失败次数</th>
        <th>最近错误</th>
        <th>寄存器值 (0~9)</th>
        <th>原始字节 HEX</th>
    </tr>
    </thead>
    <tbody>
    <!-- 行由 JS 插入 -->
    </tbody>
</table>

<footer>
    数据来源接口：<code>/api/status</code> | 刷新间隔：<span id="intervalInfo">3s</span>
</footer>

<script>
    const REFRESH_INTERVAL_MS = 3000; // 刷新间隔可改
    const tableBody = document.querySelector('#deviceTable tbody');
    const generatedAtDiv = document.getElementById('generatedAt');
    const errorDiv = document.getElementById('error');
    const intervalInfo = document.getElementById('intervalInfo');
    intervalInfo.textContent = (REFRESH_INTERVAL_MS / 1000) + 's';

    function formatTime(ts) {
        if (!ts || ts === "-") return "-";
        // ts 已是 RFC3339 字符串，直接显示或可转本地时间
        try {
            const d = new Date(ts);
            if (isNaN(d.getTime())) return ts;
            const pad = n => n.toString().padStart(2, '0');
            return d.getFullYear() + '-' + pad(d.getMonth()+1) + '-' + pad(d.getDate()) + ' ' +
                pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
        } catch (e) {
            return ts;
        }
    }

    function render(devices) {
        tableBody.innerHTML = '';
        devices.forEach(d => {
            const tr = document.createElement('tr');

            // 地址
            const tdAddr = document.createElement('td');
            tdAddr.textContent = d.address;
            tr.appendChild(tdAddr);

            // 状态
            const tdStatus = document.createElement('td');
            const span = document.createElement('span');
            if (d.online) {
                span.textContent = '在线';
                span.className = 'online';
            } else {
                span.textContent = '离线';
                span.className = 'offline';
            }
            tdStatus.appendChild(span);
            tr.appendChild(tdStatus);

            // 最近成功时间
            const tdLast = document.createElement('td');
            tdLast.textContent = formatTime(d.last_success);
            tr.appendChild(tdLast);

            // 连续失败次数
            const tdFail = document.createElement('td');
            tdFail.textContent = d.consecutive_failure;
            tr.appendChild(tdFail);

            // 最近错误
            const tdErr = document.createElement('td');
            tdErr.textContent = d.last_error ? d.last_error : '-';
            tr.appendChild(tdErr);

            // 寄存器值
            const tdRegs = document.createElement('td');
            tdRegs.className = 'reg-box';
            if (d.latest_registers && d.latest_registers.length) {
                tdRegs.textContent = d.latest_registers.map((v,i) => `[${i}: ${v}]`).join(' ');
            } else {
                tdRegs.textContent = '-';
            }
            tr.appendChild(tdRegs);

            // 原始 HEX
            const tdHex = document.createElement('td');
            tdHex.className = 'hex-box';
            tdHex.textContent = d.latest_raw_hex && d.latest_raw_hex !== '-' ? d.latest_raw_hex : '-';
            tr.appendChild(tdHex);

            tableBody.appendChild(tr);
        });
        generatedAtDiv.textContent = '生成时间：' + formatTime(new Date().toISOString());
    }

    async function fetchStatus() {
        try {
            errorDiv.textContent = '';
            const resp = await fetch('/api/status?_t=' + Date.now(), {cache: 'no-store'});
            if (!resp.ok) {
                throw new Error('HTTP ' + resp.status);
            }
            const data = await resp.json();
            render(data);
        } catch (e) {
            errorDiv.textContent = '获取数据失败：' + e.message;
        }
    }

    // 初次加载 + 周期刷新
    fetchStatus();
    setInterval(fetchStatus, REFRESH_INTERVAL_MS);
</script>
</body>
</html>